///////////////////////////////////////////////////////////////////////////////
//    Copyright (c) 2021 CASTest Corporation Limited. All Rights Reserved    //
///////////////////////////////////////////////////////////////////////////////

#ifndef NETLIST_H
#define NETLIST_H

#include "Gate.h"
#include "Cell.h"
#include "Fault.h"
#include "Parameters.h"
#include "Utils.h"
#include "Errors.h"

class Netlist {

public:
    Netlist();
    ~Netlist();
    // basic set/get functions
    void    SetNumNet(int num_net)  { _num_net = num_net; }
    int     GetNumNet() const   { return _num_net; }
    void    SetNumPi(int num_pi)    { _num_pi = num_pi; }
    int     GetNumPi() const    { return _num_pi; }
    void    SetNumPo(int num_po)    { _num_po = num_po; }
    int     GetNumPo() const    { return _num_po; }
    void    SetMaxLevel(int level)  { _max_level = level; }
    int     GetMaxLevel() const { return _max_level; }
    void    SetNetlist(Gate* gptr)  { _netlist.push_back( gptr ); }
    vector<Gate*>& GetNetlist() { return _netlist; }
    void SetPi(Gate* gptr)  { _pi.push_back( gptr ); }
    vector<Gate*>& GetPi()  { return _pi; }
    void SetPo(Gate* gptr)  { _po.push_back( gptr ); }
    vector<Gate*>& GetPo()  { return _po; }
    void SetPpi(Gate* gptr) { _ppi.push_back( gptr ); }
    vector<Gate*>& GetPpi() { return _ppi; }
    void SetPpo(Gate* gptr) { _ppo.push_back( gptr ); }
    vector<Gate*>& GetPpo() { return _ppo; }
    void SetSupply(Gate* gptr)  { _supply.push_back( gptr ); }
    vector<Gate*>& GetSupply()  { return _supply; }
    void SetStems(Gate* gptr)   { _stems.push_back( gptr ); }
    vector<Gate*>& GetStems()   { return _stems; }
    void SetNetname(int idx, string name) { _netnames.insert( {idx, name} ); }
    string& GetNetname(int idx) { return _netnames[idx]; }
    unordered_map<int, string>& GetAllNetnames() { return _netnames; }
    vector<vector<Gate*>>& GetLevels() { return _levels; }

public:
    // parsing vy format file
    void ParseNetlist(const string& primitive_vy_path,
                      const string& cell_vy_path);
    // parsing bench format file
    void ParseBench(const string& primitive_vy_path);
    // initial circuit
    void InitNetlist();
    // set gate
    void SetGlobalGate(int gate_id); // clk, set, reset
    // set gate
    void SetSpecialGate(int gate_id, SpecialGate gate_type);
    // read primitive vy file
    void ReadPrimitiveVy(const string& vy_file_path);
    // setup gate fanin and fanout connection relations
    void BuildConnection();
    // add po gates
    void AddPO();
    // add stem gates
    void BuildStem();
    // set cell border
    void SetCellBorder();
    // circuit levelize
    void SetDPI();
    void SetLevelArray();
    void SetDPO();
    void Levelize();
    void SetDFFMasterMap();
    // convert uint_8 to str
    string ConvertGateValue(Gate* gptr);
    // scan chain trace
    void TracePrimitiveScanChain(vector<int> test_so_ids,
                                 bool is_mux_input_a);
    // print circuit parser information
    void PrintNetlistInfo(ostream& out);
    void PrintGateFromPI(ostream& out);
    void PrintGateFromPO(ostream& out);
    void PrintLevelArray(ostream& out);
    void PrintTestability(ostream& out);
    void PrintGateVal(ostream& out);
protected:
    // primitive vy related data
    int _num_net;                              // nets nums read from vy plus stem branch
    int _num_pi;                               // the nums of circuit primary inputs
    int _num_po;                               // the nums of circuit primary outputs
    int _max_level;                            // the max level of circuit
    vector<Gate*> _netlist;                    // all gates and their connections
    vector<Gate*> _pi;                         // all circuit pi gates
    vector<Gate*> _po;                         // all circuit po gates
    vector<Gate*> _ppi;                        // all circuit ppi gates
    vector<Gate*> _ppo;                        // all circuit ppo gates
    vector<Gate*> _supply;                     // all supply0 and supply1
    vector<Gate*> _stems;                      // all stem gates (with fanout branchs)
    unordered_map<int, string> _netnames;      // all net map (id, name) read from primitives vy file
    unordered_map<int, string> _pinnames;
    vector<vector<Gate*>> _levels;             // all gates' levelized
    vector<Gate*> _dff_gates;                  // all dff gates in netlist
    vector<Gate*> _dlat_gates;                 // all dlat gates in netlist
    vector<vector<Gate*>> _scan_chains;
    // dff master slave map
    // the key is dff gate id[ slave ], value is master id
    unordered_map<int, int>     _dff_gate_id_to_master_map;

public:

    // read cell vy file
    void   ReadCellVy(const string& vy_file_path);
    string GetCellInstName(string instance_name);
    Cell*  GetCell(string instance_name);

    // basic set/get
    void SetCellsSize(int size) {  _cells_size = size; }
    int  GetCellsSize() const   { return _cells_size; }
    void SetCellNets(int id, string name)     { _cell_nets[id] = name; }
    unordered_map<int, string>& GetCellNets() { return _cell_nets; }
    void SetCellPins(int id, string name)     { _cell_pins[id] = name; }
    unordered_map<int, string>& GetCellPins() { return _cell_pins; }
    void SetCellNames(int id, string name)    { _cell_names[id] = name; }
    unordered_map<int, string> GetCellName()  { return _cell_names; }
    // print cell related information
    void PrintCellInfo(ostream& out);

protected:
    // cell vy related data
    vector<Cell*> _cells;                         // cell information get from cell vy file
    vector<Cell*> _dff_cells;                     // all dff cells in the circuit
    int _cells_size;                              // nums of cells
    unordered_map<int, string> _cell_nets;        // cell net id and cell net name
    unordered_map<int, string> _cell_pins;        // cell pin id and cell pin name
    unordered_map<int, string> _cell_names;       // cell id and cell name
    unordered_map<string, int> _cell_id;          // in order to quickly get cell, use a hash to store cell inst name and cell id when parse cell vy
    map<int, int> _clock_pi_ids_clock_map;        // key is clock pi id, value 1 represent 1 0 1, value 0 represent 0 1 0

};

#endif // NETLIST_H